{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Import libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import pickle\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn import pipeline, preprocessing, compose, linear_model, impute, model_selection"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Load data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>age</th>\n",
       "      <th>gender</th>\n",
       "      <th>bmi</th>\n",
       "      <th>children</th>\n",
       "      <th>smoker</th>\n",
       "      <th>region</th>\n",
       "      <th>charges</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>19</td>\n",
       "      <td>female</td>\n",
       "      <td>27.900</td>\n",
       "      <td>0</td>\n",
       "      <td>yes</td>\n",
       "      <td>southwest</td>\n",
       "      <td>16884.92400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>18</td>\n",
       "      <td>male</td>\n",
       "      <td>33.770</td>\n",
       "      <td>1</td>\n",
       "      <td>no</td>\n",
       "      <td>southeast</td>\n",
       "      <td>1725.55230</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>2</td>\n",
       "      <td>28</td>\n",
       "      <td>male</td>\n",
       "      <td>33.000</td>\n",
       "      <td>3</td>\n",
       "      <td>no</td>\n",
       "      <td>southeast</td>\n",
       "      <td>4449.46200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>3</td>\n",
       "      <td>33</td>\n",
       "      <td>male</td>\n",
       "      <td>22.705</td>\n",
       "      <td>0</td>\n",
       "      <td>no</td>\n",
       "      <td>northwest</td>\n",
       "      <td>21984.47061</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>4</td>\n",
       "      <td>32</td>\n",
       "      <td>male</td>\n",
       "      <td>28.880</td>\n",
       "      <td>0</td>\n",
       "      <td>no</td>\n",
       "      <td>northwest</td>\n",
       "      <td>3866.85520</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   age  gender     bmi  children smoker     region      charges\n",
       "0   19  female  27.900         0    yes  southwest  16884.92400\n",
       "1   18    male  33.770         1     no  southeast   1725.55230\n",
       "2   28    male  33.000         3     no  southeast   4449.46200\n",
       "3   33    male  22.705         0     no  northwest  21984.47061\n",
       "4   32    male  28.880         0     no  northwest   3866.85520"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pd.read_csv(\"/data/insurance.csv\")\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create X - features and y - target variable. Take log transformation to tackle outliners."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "target = \"charges\"\n",
    "y = np.log10(df[target])\n",
    "X = df.drop(columns=[target])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Identitify categorical columns and numeric columns. We will apply imputer (to replace null values) and one-hot encoding to categorical columns and imputer (to replace null values), polynomial transformation and standard scaler (z-scoring) to numeric values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "cat_columns = [\"gender\", \"smoker\", \"region\"]\n",
    "num_columns = [\"age\", \"bmi\", \"children\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Build pipeline for numeric and categorical variables. Search over a hyper parameter space to tune the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fitting 5 folds for each of 200 candidates, totalling 1000 fits\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=8)]: Using backend LokyBackend with 8 concurrent workers.\n",
      "[Parallel(n_jobs=8)]: Done  34 tasks      | elapsed:    1.6s\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.7706238513668783 {'est__alpha': 0.001513687811857405, 'est__l1_ratio': 0.0001}\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=8)]: Done 1000 out of 1000 | elapsed:    4.3s finished\n"
     ]
    }
   ],
   "source": [
    "cat_pipe = pipeline.Pipeline([\n",
    "    ('imputer', impute.SimpleImputer(strategy='constant', fill_value='missing')),\n",
    "    ('onehot', preprocessing.OneHotEncoder(handle_unknown='error', drop=\"first\"))\n",
    "]) \n",
    "\n",
    "num_pipe = pipeline.Pipeline([\n",
    "    ('imputer', impute.SimpleImputer(strategy='median')),\n",
    "    ('poly', preprocessing.PolynomialFeatures(degree=2, include_bias=False)),\n",
    "    ('scaler', preprocessing.StandardScaler()),\n",
    "])\n",
    "\n",
    "preprocessing_pipe = compose.ColumnTransformer([\n",
    "    (\"cat\", cat_pipe, cat_columns),\n",
    "    (\"num\", num_pipe, num_columns)\n",
    "])\n",
    "\n",
    "\n",
    "estimator_pipe = pipeline.Pipeline([\n",
    "    (\"preprocessing\", preprocessing_pipe),\n",
    "    (\"est\", linear_model.ElasticNet(random_state=1))\n",
    "])\n",
    "\n",
    "\n",
    "param_grid = {\n",
    "    \"est__alpha\": 0.0 + np.random.random(10) * 0.02,\n",
    "    \"est__l1_ratio\": np.linspace(0.0001, 1, 20),\n",
    "}\n",
    "\n",
    "\n",
    "gsearch = model_selection.GridSearchCV(estimator_pipe, param_grid, cv = 5, verbose=1, n_jobs=8)\n",
    "\n",
    "gsearch.fit(X, y)\n",
    "\n",
    "print(gsearch.best_score_, gsearch.best_params_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Find estimated values. Since we did not create tran-test split manually, we would get the estimated outcome of the entire dataset. Plot the residuals."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Residual Plot')"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "y_pred = gsearch.predict(X)\n",
    "plt.scatter(y, y_pred - y)\n",
    "plt.xlabel(\"Actual\")\n",
    "plt.ylabel(\"Residual\")\n",
    "plt.title(\"Residual Plot\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Show the few actual values vs predicted values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>actual</th>\n",
       "      <th>predict</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>49</td>\n",
       "      <td>4.587814</td>\n",
       "      <td>4.444098</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>45</td>\n",
       "      <td>4.314505</td>\n",
       "      <td>4.018335</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>665</td>\n",
       "      <td>4.629006</td>\n",
       "      <td>4.595823</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1097</td>\n",
       "      <td>3.223919</td>\n",
       "      <td>3.459821</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>584</td>\n",
       "      <td>3.094407</td>\n",
       "      <td>3.309120</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>470</td>\n",
       "      <td>3.397425</td>\n",
       "      <td>3.545149</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>309</td>\n",
       "      <td>3.889254</td>\n",
       "      <td>3.949224</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>179</td>\n",
       "      <td>3.931371</td>\n",
       "      <td>4.007766</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>917</td>\n",
       "      <td>4.544928</td>\n",
       "      <td>4.508985</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>991</td>\n",
       "      <td>3.853994</td>\n",
       "      <td>3.902824</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        actual   predict\n",
       "49    4.587814  4.444098\n",
       "45    4.314505  4.018335\n",
       "665   4.629006  4.595823\n",
       "1097  3.223919  3.459821\n",
       "584   3.094407  3.309120\n",
       "470   3.397425  3.545149\n",
       "309   3.889254  3.949224\n",
       "179   3.931371  4.007766\n",
       "917   4.544928  4.508985\n",
       "991   3.853994  3.902824"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.DataFrame({\"actual\": y, \"predict\": y_pred}).sample(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Save the model as pickle file, so that during prediction we can re-use the trained model without training the model again from scratch."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(r\"/tmp/model.pickle\", \"wb\") as f:\n",
    "    pickle.dump(gsearch, f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reload the model from the disk. In real-use case, probably you will keep the following lines and their dependencies in a seperate script file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pickle\n",
    "import pandas as pd\n",
    "with open(r\"/tmp/model.pickle\", \"rb\") as f:\n",
    "    est = pickle.load(f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a single record with the feature values to get the estimate. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'age': 18,\n",
       " 'gender': 'male',\n",
       " 'bmi': 33.0,\n",
       " 'smoker': 'no',\n",
       " 'children': 1,\n",
       " 'region': 'southeast'}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "record = {\"age\": 18, \"gender\": \"male\", \"bmi\": 33.0, \"smoker\": \"no\", \"children\": 1, \"region\": \"southeast\"}\n",
    "record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a dataframe out of the record."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>age</th>\n",
       "      <th>gender</th>\n",
       "      <th>bmi</th>\n",
       "      <th>smoker</th>\n",
       "      <th>children</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>18</td>\n",
       "      <td>male</td>\n",
       "      <td>33.0</td>\n",
       "      <td>no</td>\n",
       "      <td>1</td>\n",
       "      <td>southeast</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   age gender   bmi smoker  children     region\n",
       "0   18   male  33.0     no         1  southeast"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_input = pd.DataFrame.from_dict([record])\n",
    "df_input"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Get the prediction for the df_input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2999.0394772])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "10 ** est.predict(df_input)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
